/* Copyright (c) 2020 XEPIC Corporation Limited */
/*
 * Copyright (c) 2001-2018 Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */

/*
 * Callbacks are objects that carry a function to be called when some
 * event in the simulation occurs. The VPI code create a __vpiCallback
 * object, and that object is put in some location that the simulation
 * can look when the event in question is tripped.
 */

#include "config.h"
#include "event.h"
#include "schedule.h"
#include "vpi_priv.h"
#include "vpi_user.h"
#include "vvp_net.h"
#include "vvp_net_sig.h"
#ifdef CHECK_WITH_VALGRIND
#include "vvp_cleanup.h"
#endif
#include <cassert>
#include <cstdio>
#include <cstdlib>
/*
 * Callback handles are created when the VPI function registers a
 * callback. The handle is stored by the run time, and it triggered
 * when the run-time thing that it is waiting for happens.
 *
 * This is the thing that the VPI code references by the vpiHandle. It
 * also points to callback data that the caller attached to the event,
 * as well as the time structure to receive data.
 *
 * The cb_sync is a private member that points to the schedulable
 * event that is triggered when the event happens. The sync_cb class
 * represents the action to execute when the scheduler gets to this
 * event. This member is only used for things like cbReadOnlySync.
 */

class sync_callback;

struct sync_cb : public vvp_gen_event_s {
  sync_callback* handle;
  bool sync_flag;

  ~sync_cb() {}

  virtual void run_run();
};

inline __vpiCallback::__vpiCallback() { next = 0; }

__vpiCallback::~__vpiCallback() {}

int __vpiCallback::get_type_code(void) const { return vpiCallback; }

value_callback::value_callback(p_cb_data data) {
  cb_data = *data;
  if (data->time) {
    cb_time = *(data->time);
  } else {
    cb_time.type = vpiSuppressTime;
  }
  cb_data.time = &cb_time;
  if (data->value) {
    cb_value = *(data->value);
  } else {
    cb_value.format = vpiSuppressVal;
  }
  cb_data.value = &cb_value;
}

/*
 * Normally, any assign to a value triggers a value change callback,
 * so return a constant true here. This is a stub.
 */
bool value_callback::test_value_callback_ready(void) { return true; }

static void vpip_real_value_change(value_callback* cbh, vpiHandle ref) {
  struct __vpiRealVar* rfp = dynamic_cast<__vpiRealVar*>(ref);
  assert(rfp);
  vvp_vpi_callback* obj = dynamic_cast<vvp_vpi_callback*>(rfp->net->fil);
  assert(obj);

  obj->add_vpi_callback(cbh);
}

class value_part_callback : public value_callback {
 public:
  explicit value_part_callback(p_cb_data data);
  ~value_part_callback();

  bool test_value_callback_ready(void);

 private:
  char* value_bits_;
  size_t value_off_;
};

inline value_part_callback::value_part_callback(p_cb_data data)
    : value_callback(data) {
  struct __vpiPV* pobj = dynamic_cast<__vpiPV*>(data->obj);
  assert(pobj);

  vvp_vpi_callback* sig_fil;
  sig_fil = dynamic_cast<vvp_vpi_callback*>(pobj->net->fil);
  assert(sig_fil);

  sig_fil->add_vpi_callback(this);
  // Get a reference value that can be used to compare with an
  // updated value. Use the filter get_value to get the value,
  // and get it in BinStr form so that compares are easy. Note
  // that the vpiBinStr format has the MSB first, but the tbase
  // is lsb first.
  s_vpi_value tmp_value;
  tmp_value.format = vpiBinStrVal;
  sig_fil->get_value(&tmp_value);

  value_bits_ = new char[pobj->width + 1];
  value_off_ = pobj->parent->vpi_get(vpiSize) - pobj->width - pobj->tbase;

  memcpy(value_bits_, tmp_value.value.str + value_off_, pobj->width);
  value_bits_[pobj->width] = 0;
}

value_part_callback::~value_part_callback() { delete[] value_bits_; }

bool value_part_callback::test_value_callback_ready(void) {
  struct __vpiPV* pobj = dynamic_cast<__vpiPV*>(cb_data.obj);
  assert(pobj);

  vvp_vpi_callback* sig_fil;
  sig_fil = dynamic_cast<vvp_vpi_callback*>(pobj->net->fil);
  assert(sig_fil);

  // Get a reference value that can be used to compare with an
  // updated value.
  s_vpi_value tmp_value;
  tmp_value.format = vpiBinStrVal;
  sig_fil->get_value(&tmp_value);

  if (memcmp(value_bits_, tmp_value.value.str + value_off_, pobj->width) == 0)
    return false;

  memcpy(value_bits_, tmp_value.value.str + value_off_, pobj->width);
  return true;
}

/*
 * Attach the __vpiCallback to the object that this part select
 * selects from. The part select itself is not a vvp_vpi_callback
 * object, but it refers to a net that is a vvp_vpi_callback, so
 * add the callback to that object.
 */
static value_callback* make_value_change_part(p_cb_data data) {
  /* Attach the __vpiCallback object to the signal. */
  value_callback* cbh = new value_part_callback(data);
  return cbh;
}

/*
 * A value change callback is tripped when a bit of a signal
 * changes. This function creates that value change callback and
 * attaches it to the relevant vpiSignal object. Also, if the signal
 * does not already have them, create some callback functors to do the
 * actual value change detection.
 */
static value_callback* make_value_change(p_cb_data data) {
  if (vpi_get(vpiAutomatic, data->obj)) {
    fprintf(stderr,
            "vpi error: cannot place value change "
            "callback on automatically allocated "
            "variable '%s'\n",
            vpi_get_str(vpiName, data->obj));
    return 0;
  }

  // Special case: the target object is a vpiPartSelect
  if (data->obj->get_type_code() == vpiPartSelect) {
    if (data->obj->vpi_handle(vpiArray))
      return vpip_array_word_change(data);
    else
      return make_value_change_part(data);
  }

  if (data->obj->get_type_code() == vpiMemoryWord)
    return vpip_array_word_change(data);

  if (data->obj->get_type_code() == vpiMemory) return vpip_array_change(data);

  value_callback* obj = new value_callback(data);

  assert(data->obj);
  switch (data->obj->get_type_code()) {
    case vpiReg:
    case vpiNet:
    case vpiIntegerVar:
    case vpiBitVar:
    case vpiByteVar:
    case vpiShortIntVar:
    case vpiIntVar:
    case vpiLongIntVar:
      /* Attach the callback to the vvp_fun_signal node by
         putting it in the vpi_callbacks list. */
      struct __vpiSignal* sig;
      sig = dynamic_cast<__vpiSignal*>(data->obj);

      vvp_net_fil_t* sig_fil;
      sig_fil = dynamic_cast<vvp_net_fil_t*>(sig->node->fil);
      assert(sig_fil);

      /* Attach the __vpiCallback object to the signal. */
      sig_fil->add_vpi_callback(obj);
      break;

    case vpiRealVar:
      vpip_real_value_change(obj, data->obj);
      break;

    case vpiNamedEvent:
      __vpiNamedEvent* nev;
      nev = dynamic_cast<__vpiNamedEvent*>(data->obj);
      nev->add_vpi_callback(obj);
      break;

    case vpiModule:
    case vpiConstant:
    case vpiParameter:
      /* These are constant, so there are no value change
         lists to put them in. */
      break;

    default:
      fprintf(stderr,
              "make_value_change: sorry: I cannot callback "
              "values on type code=%d\n",
              data->obj->get_type_code());
      delete obj;
      return 0;
  }

  return obj;
}

class sync_callback : public __vpiCallback {
 public:
  explicit sync_callback(p_cb_data data);
  ~sync_callback();

 public:
  // scheduled event
  struct sync_cb* cb_sync;
  // user supplied callback data
  struct t_vpi_time cb_time;

 private:
};

inline sync_callback::sync_callback(p_cb_data data) {
  cb_sync = 0;

  cb_data = *data;
  assert(data->time);
  cb_time = *(data->time);
  cb_data.time = &cb_time;
}

sync_callback::~sync_callback() { delete cb_sync; }

void sync_cb::run_run() {
  if (handle == 0) return;

  sync_callback* cur = handle;
  cur->cb_data.time->type = vpiSimTime;
  vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());

  /* Run the callback. If the cb_rtn function pointer is set to
     null, then just skip the whole thing and free it. This is
     the usual way to cancel one-time callbacks of this sort. */
  if (cur->cb_data.cb_rtn != 0) {
    assert(vpi_mode_flag == VPI_MODE_NONE);
    vpi_mode_flag = sync_flag ? VPI_MODE_ROSYNC : VPI_MODE_RWSYNC;
    vpip_cur_task = dynamic_cast<__vpiSysTaskCall*>(cur->cb_data.obj);
    (cur->cb_data.cb_rtn)(&cur->cb_data);
    vpip_cur_task = 0;
    vpi_mode_flag = VPI_MODE_NONE;
  }

  delete cur;
}

static sync_callback* make_sync(p_cb_data data, bool readonly_flag) {
  sync_callback* obj = new sync_callback(data);

  struct sync_cb* cb = new sync_cb;
  cb->sync_flag = readonly_flag ? true : false;
  cb->handle = obj;
  obj->cb_sync = cb;

  vvp_time64_t tv = 0;
  switch (obj->cb_time.type) {
    case vpiSuppressTime:
      break;

    case vpiSimTime:
      tv = vpip_timestruct_to_time(&obj->cb_time);
      break;

    default:
      fprintf(stderr, "Unsupported time type %d.\n", (int)obj->cb_time.type);
      assert(0);
      break;
  }
  schedule_generic(cb, tv, true, readonly_flag);
  return obj;
}

static struct __vpiCallback* make_afterdelay(p_cb_data data) {
  sync_callback* obj = new sync_callback(data);
  struct sync_cb* cb = new sync_cb;
  cb->sync_flag = false;
  cb->handle = obj;
  obj->cb_sync = cb;

  vvp_time64_t tv = 0;
  switch (obj->cb_time.type) {
    case vpiSimTime:
      tv = vpip_timestruct_to_time(&obj->cb_time);
      break;

    default:
      fprintf(stderr, "Unsupported time type %d.\n", (int)obj->cb_time.type);
      assert(0);
      break;
  }

  schedule_generic(cb, tv, false);

  return obj;
}

static struct __vpiCallback* make_at_start_of_sim_time(p_cb_data data) {
  sync_callback* obj = new sync_callback(data);
  struct sync_cb* cb = new sync_cb;
  cb->sync_flag = false;
  cb->handle = obj;
  obj->cb_sync = cb;

  vvp_time64_t tv = 0;
  switch (obj->cb_time.type) {
    case vpiSimTime:
      tv = vpip_timestruct_to_time(&obj->cb_time);
      break;

    default:
      fprintf(stderr, "Unsupported time type %d.\n", (int)obj->cb_time.type);
      assert(0);
      break;
  }

  vvp_time64_t cur = schedule_simtime();
  if (cur > tv) {
    tv = 0;
    assert(0);
  } else if (cur == tv) {
    tv = 0;
  } else {
    tv -= cur;
  }
  schedule_at_start_of_simtime(cb, tv);

  return obj;
}

static struct __vpiCallback* make_at_end_of_sim_time(p_cb_data data) {
  sync_callback* obj = new sync_callback(data);
  struct sync_cb* cb = new sync_cb;
  cb->sync_flag = false;
  cb->handle = obj;
  obj->cb_sync = cb;

  vvp_time64_t tv = 0;
  switch (obj->cb_time.type) {
    case vpiSimTime:
      tv = vpip_timestruct_to_time(&obj->cb_time);
      break;

    default:
      fprintf(stderr, "Unsupported time type %d.\n", (int)obj->cb_time.type);
      assert(0);
      break;
  }

  vvp_time64_t cur = schedule_simtime();
  if (cur > tv) {
    tv = 0;
    assert(0);
  } else if (cur == tv) {
    tv = 0;
  } else {
    tv -= cur;
  }
  schedule_at_end_of_simtime(cb, tv);

  return obj;
}

/*
 * The following functions are the used for pre and post simulation
 * callbacks.
 */

class simulator_callback : public __vpiCallback {
 public:
  inline explicit simulator_callback(struct t_cb_data* data) {
    cb_data = *data;
  }

 public:
};

static simulator_callback* NextSimTime = 0;
static simulator_callback* EndOfCompile = 0;
static simulator_callback* StartOfSimulation = 0;
static simulator_callback* EndOfSimulation = 0;

#ifdef CHECK_WITH_VALGRIND
/* This is really only needed if the simulator aborts before starting the
 * main event loop. For that reason we can skip the next sim time queue. */
void simulator_cb_delete(void) {
  simulator_callback* cur;

  /* Delete all the end of compile callbacks. */
  while (EndOfCompile) {
    cur = EndOfCompile;
    EndOfCompile = dynamic_cast<simulator_callback*>(cur->next);
    delete cur;
  }

  /* Delete all the start of simulation callbacks. */
  while (StartOfSimulation) {
    cur = StartOfSimulation;
    StartOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
    delete cur;
  }

  /* Delete all the end of simulation callbacks. */
  while (EndOfSimulation) {
    cur = EndOfSimulation;
    EndOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
    delete cur;
  }
}
#endif

void vpiEndOfCompile(void) {
  simulator_callback* cur;

  /*
   * Walk the list of register callbacks, executing them and
   * freeing them when done.
   */
  assert(vpi_mode_flag == VPI_MODE_NONE);
  vpi_mode_flag = VPI_MODE_RWSYNC;

  while (EndOfCompile) {
    cur = EndOfCompile;
    EndOfCompile = dynamic_cast<simulator_callback*>(cur->next);
    (cur->cb_data.cb_rtn)(&cur->cb_data);
    delete cur;
  }

  vpi_mode_flag = VPI_MODE_NONE;
}

void vpiStartOfSim(void) {
  simulator_callback* cur;

  /*
   * Walk the list of register callbacks, executing them and
   * freeing them when done.
   */
  assert(vpi_mode_flag == VPI_MODE_NONE);
  vpi_mode_flag = VPI_MODE_RWSYNC;

  while (StartOfSimulation) {
    cur = StartOfSimulation;
    StartOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
    (cur->cb_data.cb_rtn)(&cur->cb_data);
    delete cur;
  }

  vpi_mode_flag = VPI_MODE_NONE;
}

void vpiPostsim(void) {
  simulator_callback* cur;

  /*
   * Walk the list of register callbacks
   */
  assert(vpi_mode_flag == VPI_MODE_NONE);
  vpi_mode_flag = VPI_MODE_ROSYNC;

  while (EndOfSimulation) {
    cur = EndOfSimulation;
    EndOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
    /* Only set the time if it is not NULL. */
    if (cur->cb_data.time)
      vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
    (cur->cb_data.cb_rtn)(&cur->cb_data);
    delete cur;
  }

  vpi_mode_flag = VPI_MODE_NONE;
}

/*
 * The scheduler invokes this to clear out callbacks for the next
 * simulation time.
 */
void vpiNextSimTime(void) {
  simulator_callback* cur;

  assert(vpi_mode_flag == VPI_MODE_NONE);
  vpi_mode_flag = VPI_MODE_RWSYNC;

  while (NextSimTime) {
    cur = NextSimTime;
    NextSimTime = dynamic_cast<simulator_callback*>(cur->next);
    (cur->cb_data.cb_rtn)(&cur->cb_data);
    delete cur;
  }

  vpi_mode_flag = VPI_MODE_NONE;
}

static simulator_callback* make_prepost(p_cb_data data) {
  simulator_callback* obj = new simulator_callback(data);

  /* Insert at head of list */
  switch (data->reason) {
    case cbEndOfCompile:
      obj->next = EndOfCompile;
      EndOfCompile = obj;
      break;
    case cbStartOfSimulation:
      obj->next = StartOfSimulation;
      StartOfSimulation = obj;
      break;
    case cbEndOfSimulation:
      obj->next = EndOfSimulation;
      EndOfSimulation = obj;
      break;
    case cbNextSimTime:
      obj->next = NextSimTime;
      NextSimTime = obj;
  }

  return obj;
}

vpiHandle vpi_register_cb(p_cb_data data) {
  struct __vpiCallback* obj = 0;

  assert(data);
  switch (data->reason) {
    case cbValueChange:
      obj = make_value_change(data);
      break;

    case cbReadOnlySynch:
      obj = make_sync(data, true);
      break;

    case cbReadWriteSynch:
      obj = make_sync(data, false);
      break;

    case cbAtStartOfSimTime:
      obj = make_at_start_of_sim_time(data);
      break;

    case cbAtEndOfSimTime:
      obj = make_at_end_of_sim_time(data);
      break;

    case cbAfterDelay:
      obj = make_afterdelay(data);
      break;

    case cbEndOfCompile:
    case cbStartOfSimulation:
    case cbEndOfSimulation:
    case cbNextSimTime:
      obj = make_prepost(data);
      break;

    default:
      fprintf(stderr,
              "vpi error: vpi_register_cb invalid or "
              "unsupported callback reason: %d\n",
              (int)data->reason);
      break;
  }

  return obj;
}

/*
 * Removing a callback doesn't really delete it right away. Instead,
 * it clears the reference to the user callback function. This causes
 * the callback to quietly reap itself.
 */
PLI_INT32 vpi_remove_cb(vpiHandle ref) {
  struct __vpiCallback* obj = dynamic_cast<__vpiCallback*>(ref);
  assert(obj);
  obj->cb_data.cb_rtn = 0;

  return 1;
}

void callback_execute(struct __vpiCallback* cur) {
  const vpi_mode_t save_mode = vpi_mode_flag;
  vpi_mode_flag = VPI_MODE_RWSYNC;

  assert(cur->cb_data.cb_rtn);
  switch (cur->cb_data.time->type) {
    case vpiSimTime:
      vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
      break;
    case vpiScaledRealTime: {
      cur->cb_data.time->real = vpip_time_to_scaled_real(
          schedule_simtime(),
          (__vpiScope*)vpi_handle(vpiScope, cur->cb_data.obj));
      break;
    }
    case vpiSuppressTime:
      break;
    default:
      fprintf(stderr, "Unsupported time format %d.\n",
              (int)cur->cb_data.time->type);
      assert(0);
      break;
  }
  (cur->cb_data.cb_rtn)(&cur->cb_data);

  vpi_mode_flag = save_mode;
}

/*
 * Usually there is at most one array word associated with a vvp signal, but
 * due to port collapsing, there may be more. Using a linked list to record
 * the array words minimises memory use for the most common case (no array
 * words) or next most common case (one array word).
 */
struct __vpi_array_word {
  struct __vpi_array_word* next;
  struct __vpiArray* array;
  unsigned long word;
};

vvp_vpi_callback::vvp_vpi_callback() {
  vpi_callbacks_ = 0;
  array_words_ = 0;
}

vvp_vpi_callback::~vvp_vpi_callback() {
  assert(vpi_callbacks_ == 0);
  assert(array_words_ == 0);
}

void vvp_vpi_callback::attach_as_word(vvp_array_t arr, unsigned long addr) {
  struct __vpi_array_word* tmp = new __vpi_array_word;
  tmp->array = arr;
  tmp->word = addr;
  tmp->next = array_words_;
  array_words_ = tmp;
}

void vvp_vpi_callback::add_vpi_callback(value_callback* cb) {
  cb->next = vpi_callbacks_;
  vpi_callbacks_ = cb;
}

#ifdef CHECK_WITH_VALGRIND
void vvp_vpi_callback::clear_all_callbacks() {
  while (vpi_callbacks_) {
    value_callback* tmp = dynamic_cast<value_callback*>(vpi_callbacks_->next);
    delete vpi_callbacks_;
    vpi_callbacks_ = tmp;
  }
  while (array_words_) {
    struct __vpi_array_word* tmp = array_words_->next;
    delete array_words_;
    array_words_ = tmp;
  }
}
#endif

/*
 * A vvp_fun_signal uses this method to run its callbacks whenever it
 * has a value change. If the cb_rtn is non-nil, then call the
 * callback function. If the cb_rtn pointer is nil, then the object
 * has been marked for deletion. Free it.
 */
void vvp_vpi_callback::run_vpi_callbacks() {
  struct __vpi_array_word* array_word = array_words_;
  while (array_word) {
    array_word->array->word_change(array_word->word);
    array_word = array_word->next;
  }

  value_callback* next = vpi_callbacks_;
  value_callback* prev = 0;

  while (next) {
    value_callback* cur = next;
    next = dynamic_cast<value_callback*>(cur->next);

    if (cur->cb_data.cb_rtn != 0) {
      if (cur->test_value_callback_ready()) {
        if (cur->cb_data.value) get_value(cur->cb_data.value);

        callback_execute(cur);
      }
      prev = cur;

    } else if (prev == 0) {
      vpi_callbacks_ = next;
      cur->next = 0;
      delete cur;

    } else {
      assert(prev->next == cur);
      prev->next = next;
      cur->next = 0;
      delete cur;
    }
  }
}

void vvp_signal_value::get_signal_value(struct t_vpi_value* vp) {
  switch (vp->format) {
    case vpiScalarVal:
      // This works because vvp_bit4_t has the same encoding
      // as a scalar value! See vpip_vec4_get_value() for a
      // more robust method.
      vp->value.scalar = value(0);
      break;

    case vpiBinStrVal:
    case vpiOctStrVal:
    case vpiDecStrVal:
    case vpiHexStrVal:
    case vpiIntVal:
    case vpiVectorVal:
    case vpiStringVal:
    case vpiRealVal: {
      unsigned wid = value_size();
      vvp_vector4_t vec4(wid);
      for (unsigned idx = 0; idx < wid; idx += 1) {
        vec4.set_bit(idx, value(idx));
      }
      vpip_vec4_get_value(vec4, wid, false, vp);
      break;
    }

    case vpiSuppressVal:
      break;

    default:
      fprintf(stderr,
              "vpi_callback: value "
              "format %d not supported (fun_signal)\n",
              (int)vp->format);
  }
}

static double vlg_round(double rval) {
  if (rval >= 0.0) {
    return floor(rval + 0.5);
  } else {
    return ceil(rval - 0.5);
  }
}

static void real_signal_value(struct t_vpi_value* vp, double rval) {
  char* rbuf = (char*)need_result_buf(64 + 1, RBUF_VAL);

  switch (vp->format) {
    case vpiObjTypeVal:
      vp->format = vpiRealVal;
      // fallthrough
    case vpiRealVal:
      vp->value.real = rval;
      break;

    case vpiIntVal:
      /* NaN or +/- infinity are translated as 0. */
      if (rval != rval || (rval && (rval == 0.5 * rval))) {
        rval = 0.0;
      } else {
        rval = vlg_round(rval);
      }
      vp->value.integer = (PLI_INT32)rval;
      break;

    case vpiDecStrVal:
      if (::isnan(rval))
        sprintf(rbuf, "%s", "nan");
      else
        sprintf(rbuf, "%0.0f", vlg_round(rval));
      vp->value.str = rbuf;
      break;

    case vpiHexStrVal:
      sprintf(rbuf, "%" PRIx64, (uint64_t)vlg_round(rval));
      vp->value.str = rbuf;
      break;

    case vpiBinStrVal: {
      uint64_t val = (uint64_t)vlg_round(rval);
      unsigned len = 0;

      while (val > 0) {
        len += 1;
        val /= 2;
      }

      val = (uint64_t)vlg_round(rval);
      for (unsigned idx = 0; idx < len; idx += 1) {
        rbuf[len - idx - 1] = (val & 1) ? '1' : '0';
        val /= 2;
      }

      rbuf[len] = 0;
      if (len == 0) {
        rbuf[0] = '0';
        rbuf[1] = 0;
      }
      vp->value.str = rbuf;
      break;
    }

    case vpiSuppressVal:
      break;

    default:
      fprintf(stderr,
              "vpi_callback: value "
              "format %d not supported (fun_signal_real)\n",
              (int)vp->format);
  }
}

void vvp_fun_signal_real_aa::get_signal_value(struct t_vpi_value* vp) {
  real_signal_value(vp, real_value());
}

void vvp_wire_real::get_signal_value(struct t_vpi_value* vp) {
  real_signal_value(vp, real_value());
}

void vvp_fun_signal_string_aa::get_signal_value(struct t_vpi_value*) {
  assert(0);
}
#if 0
void vvp_wire_string::get_signal_value(struct t_vpi_value*vp)
{
      assert(0);
}
#endif
void vvp_wire_vec4::get_value(struct t_vpi_value* val) {
  get_signal_value(val);
}

void vvp_wire_vec8::get_value(struct t_vpi_value* val) {
  get_signal_value(val);
}

void vvp_wire_real::get_value(struct t_vpi_value* val) {
  get_signal_value(val);
}
#if 0
void vvp_wire_string::get_value(struct t_vpi_value*val)
{
      assert(0);
}
#endif
